#include <jni.h>
#include <string>
#include "md5.h"
#include <android/log.h>
#include <iostream>

#ifndef LOG_TAG
#define LOG_TAG "JNI"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG ,__VA_ARGS__) // 定义LOGD类型
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG ,__VA_ARGS__) // 定义LOGI类型
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG ,__VA_ARGS__) // 定义LOGW类型
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG ,__VA_ARGS__) // 定义LOGE类型
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,LOG_TAG ,__VA_ARGS__) // 定义LOGF类型
#endif
using namespace std;
#define MD5_KEY = "12345"

const char *PACKAGE_NAMES[] = {"com.ethan.ndkmd51", "com.ethan.ndkmd5"};
const char *SGIN_MD5S[] = {"B5EFDE763E8FB2357F3A3F30A4E615B6---","B5EFDE763E8FB2357F3A3F30A4E615B7"};
int VERIFY = 0;
const  char *SALT = "121345";

extern "C" JNIEXPORT jstring JNICALL
Java_com_ethan_ndkmd5_Md5Untils_stringFromJNI(
        JNIEnv *env,
        jclass clazz) {
    string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

void correctUtfBytes(char* bytes) {
    char three = 0;
    while (*bytes != '\0') {
        unsigned char utf8 = *(bytes++);
        three = 0;
        // Switch on the high four bits.
        switch (utf8 >> 4) {
            case 0x00:
            case 0x01:
            case 0x02:
            case 0x03:
            case 0x04:
            case 0x05:
            case 0x06:
            case 0x07:
                // Bit pattern 0xxx. No need for any extra bytes.
                break;
            case 0x08:
            case 0x09:
            case 0x0a:
            case 0x0b:
            case 0x0f:
                /*
                 * Bit pattern 10xx or 1111, which are illegal start bytes.
                 * Note: 1111 is valid for normal UTF-8, but not the
                 * modified UTF-8 used here.
                 */
                *(bytes-1) = '?';
                break;
            case 0x0e:
                // Bit pattern 1110, so there are two additional bytes.
                utf8 = *(bytes++);
                if ((utf8 & 0xc0) != 0x80) {
                    --bytes;
                    *(bytes-1) = '?';
                    break;
                }
                three = 1;
                // Fall through to take care of the final byte.
            case 0x0c:
            case 0x0d:
                // Bit pattern 110x, so there is one additional byte.
                utf8 = *(bytes++);
                if ((utf8 & 0xc0) != 0x80) {
                    --bytes;
                    if(three)--bytes;
                    *(bytes-1)='?';
                }
                break;
        }
    }
}
const char* md5encode(const string& msg){
//    MD5_CTX md5Ctx = {0};
//    MD5Init(&md5Ctx);
//    MD5Update(&md5Ctx, reinterpret_cast<unsigned char *>(source), strlen(source));
//    unsigned char dest[16] = {0};
//    MD5Final(dest, &md5Ctx);
//    int i = 0;
//    char szMd5[32] = {0};
//    for (i = 0; i < 16; i++) {
//        sprintf(szMd5, "%s%02X", szMd5, dest[i]);
//    }

    MD5(msg).toStr();

    const char* p="C20AD4D76FE97759AA27A0C99BFF6710";

    return p;
}

void verify(JNIEnv *env, jobject m_context) {
    //包名校验
    if (m_context == NULL)return;
    jclass jClz = env->GetObjectClass(m_context);
    if (jClz == NULL)return;
    jmethodID jmid = env->GetMethodID(jClz, "getPackageName", "()Ljava/lang/String;");
    if (jmid == NULL) return;
    jstring jPackageName = static_cast<jstring>(env->CallObjectMethod(m_context, jmid));
    const char *packageName = env->GetStringUTFChars(jPackageName, 0);

    int pkg_len = sizeof(PACKAGE_NAMES) / sizeof(PACKAGE_NAMES[0]);

    int pkg_count = 0;
    for (int i = 0; i < pkg_len; i++) {
        if (strcmp(packageName, PACKAGE_NAMES[i]) == 0) {
            break;
        }
        pkg_count++;
    }
    if (pkg_count==pkg_len){
        return;
    }
    //证书校验
    //PackageManager
    //jmid = env->GetMethodID(jclz, "getPackageManager", "()Landroid/content/pm/PackageManager;");
    jmethodID pm_methid = env->GetMethodID(jClz, "getPackageManager",
                                           "()Landroid/content/pm/PackageManager;");
    jobject pm = env->CallObjectMethod(m_context, pm_methid);

    //PackageInfo
    jclass pm_cls = env->GetObjectClass(pm);
    jmethodID pkg_info_methid = env->GetMethodID(pm_cls, "getPackageInfo",
                                                 "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
    jobject pkg_info = env->CallObjectMethod(pm, pkg_info_methid, jPackageName, 0x00000040);

    //Signature
    jclass sig_cls = env->GetObjectClass(pkg_info);
    jfieldID sig_field = env->GetFieldID(sig_cls, "signatures", "[Landroid/content/pm/Signature;");
    jobjectArray signatures = static_cast<jobjectArray>(env->GetObjectField(pkg_info, sig_field));
    jobject signature0 = env->GetObjectArrayElement(signatures, 0);

    //获取签名字符
    jclass sig0_cls = env->GetObjectClass(signature0);
    jmethodID sig0_methid = env->GetMethodID(sig0_cls, "toCharsString", "()Ljava/lang/String;");
    jstring sig_str = static_cast<jstring>(env->CallObjectMethod(signature0, sig0_methid));
    char *sig_str_char = const_cast<char *>(env->GetStringUTFChars(sig_str, 0));

    char *sign_md5 = const_cast<char *>(MD5(sig_str_char).toStr().c_str());
    int sign_len = sizeof(SGIN_MD5S)/ sizeof(SGIN_MD5S[0]);
    for (int i = 0; i < sign_len; i++) {
        if (strcmp(sign_md5, SGIN_MD5S[i]) == 0) {
            VERIFY=1;
            break;
        }
    }

}

extern "C"
JNIEXPORT jstring JNICALL
//md5类
Java_com_ethan_ndkmd5_Md5Untils_md5JNI(JNIEnv *env, jclass clazz, jstring source,
                                       jobject m_context) {
    // TODO: implement md5JNI()
    // char *szText = Jstring2CStr(env,source);
    verify(env, m_context);
    LOGD("-----------%d",VERIFY);
    if (VERIFY != 1) {
        return env->NewStringUTF("unauthorized");
    }
    char *szText = const_cast<char *>(env->GetStringUTFChars(source,
                                                             reinterpret_cast<jboolean *>(false)));
    string result = MD5(szText).toStr();
    env->ReleaseStringChars(source, reinterpret_cast<const jchar *>(szText));
    return env->NewStringUTF(result.c_str());
}




